#ifndef __HTTP_HPP_
#define __HTTP_HPP_

#include <iostream>
#include <string>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include<functional>

static const std::string _SepOfLine = "\r\n";
static const std::string _SepOfKV = ": ";
static const std::string _space = " ";
static const std::string _defalutpath = "./wwwroot";

class HttpRequest
{
private:
    std::string ReadOneLine(std::string &reqstr, bool *status)
    {
        auto pos = reqstr.find(_SepOfLine);
        if (pos == std::string::npos)
        {
            *status = false;
            return std::string();
        }
        *status = true;
        auto line = reqstr.substr(0, pos);
        reqstr.erase(0, pos + _SepOfLine.size());
        return line;
    }

    void Extract(std::string _line, std::string *_Key, std::string *_Value)
    {
        auto _index = _line.find(_SepOfKV);
        *_Key = _line.substr(0, _index);
        *_Value = _line.substr(_index + _SepOfKV.size());
    }

public:
    HttpRequest()
    {
    }

    ~HttpRequest()
    {
    }

    void DeSerialize(std::string &_reqstr)
    {

        bool status = true;
        std::string _firstline = ReadOneLine(_reqstr, &status);
        std::stringstream _ss(_firstline);
        _ss >> _method >> _uri >> _httpversion;

        while (1)
        {
            std::string _line = ReadOneLine(_reqstr, &status);
            if (status && !_line.empty())
            {
                std::string k, v;
                Extract(_line, &k, &v);
                if (k.empty() || v.empty())
                    continue;
                _req_header.insert(std::make_pair(k, v));
            }
            else if (status)
            {
                _sepline = _SepOfLine;
                break;
            }
            else
            {
                LOG(LogLevel::DEBUG) << "非法请求";
                break;
            }
        }
        _req_content = _reqstr;

        _path = _defalutpath;
        _path += _uri;
        if (_uri == "/")
            _path += "index.html";
        
        auto pos = _path.find("?");
        if(pos != std::string::npos)
        {
            std::string _args = _path.substr(pos+1);
            _path = _path.substr(0,pos);
            _req_content += _args;
        }            
        
    }

    std::string Args()
    {
        return _req_content;
    }



    std::string Path()
    {
        return _path;
    }

    void SetPath(const std::string &path)
    {
        _path = path;
    }

private:
    std::string _method;
    std::string _uri;
    std::string _httpversion;
    std::unordered_map<std::string, std::string> _req_header;
    std::string _sepline;
    std::string _req_content;

    std::string _path;
};

class HttpResponse
{
private:
    std::string Describe(int code)
    {
        switch (code)
        {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        case 300:
            return "Bad Request";
        case 302:
            return "Found";
        default:
            return "";
        }
    }

public:
    HttpResponse()
        : _httpversion("HTTP/1.1"), _sepline("\r\n")
    {
    }

    ~HttpResponse()
    {
    }

    std::string Serialize()
    {
        std::string _respline = _httpversion + _space + std::to_string(_code) + _space + _desc + _SepOfLine;
        for (auto &e : _resp_header)
        {
            _respline += (e.first + ": " + e.second);
            _respline += _sepline;
        }
        _respline += _resp_content;

        return _respline;
    }

    bool Read(const std::string &_path)
    {

        std::ifstream file(_path, std::ios::binary | std::ios::ate);
        if (!file.is_open())
        {
            return false;
        }

        file.seekg(0, std::ios::end);
        std::streamsize size = file.tellg();
        file.seekg(0, std::ios::beg);

        std::string content;
        content.resize(size);

        if (file.read(&content[0], size))
        {
            // 成功读取
            _resp_content = content;
        }

        file.close();

        return true;
    }

    void SetKV(std::string _Key, std::string _Value)
    {
        _resp_header[_Key] = _Value;
    }

    void SetCode(int code)
    {
        _code = code;
        _desc = Describe(_code);
    }

    size_t ContentLength()
    {
        return _resp_content.size();
    }

    std::string ContentTypeV(const std::string &_ret)
    {
        if (_ret == "html")
        {
            return "test/html";
        }
        else if (_ret == "txt")
        {
            return "text/plain";
        }
        return "";
    }

    std::string Suffix(const std::string &_path)
    {
        auto pos = _path.rfind('.');
        std::string ret = _path.substr(pos + 1);
        return ContentTypeV(ret);
    }

private:
    std::string _httpversion;
    int _code;
    std::string _desc;
    std::unordered_map<std::string, std::string> _resp_header;
    std::string _sepline;
    std::string _resp_content;
};

using funct_t = std::function<HttpResponse(HttpRequest&)>;


class http
{
public:
    http()
    {
    }

    void Resigter(const std::string& _Key,funct_t _Value)
    {
        std::string _key = _defalutpath;
        _key += _Key;
        _function[_key] = _Value;
    }


    std::string CreateResponse(std::string &_reqline)
    {
        HttpRequest _req;
        _req.DeSerialize(_reqline);

        HttpResponse _resp;
        std::string _path = _req.Path();
        if(_function.find(_path) != _function.end())
        {
            _resp = _function[_path](_req);
        }
        
        std::string resp = _resp.Serialize();
        return resp;
    }

    ~http()
    {
    }


private:
    std::unordered_map<std::string,funct_t> _function;
};

#endif